package com.fingard.dsp.bank.directbank.bfb01;

import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;

import com.fingard.constant.Format;
import com.fingard.crypto.CertHelper;
import com.fingard.crypto.DigestHelper;
import com.fingard.crypto.SignVerifyHelper;
import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.FailCode;
import com.fingard.dsp.bank.directbank.PaymentState;
import com.fingard.dsp.bank.directbank.bankInterface.IQRPay;
import com.fingard.dsp.bank.reqrespobj.qrpay.ReqGenQRInfo;
import com.fingard.dsp.bank.reqrespobj.qrpay.RetAtsGenQRInfo;
import com.fingard.dsp.bank.reqrespobj.unifiedorder.ReqQueryOrder;
import com.fingard.dsp.bank.reqrespobj.unifiedorder.ReqUnifiedOrder;
import com.fingard.dsp.bank.reqrespobj.unifiedorder.RetAtsQueryOrder;
import com.fingard.dsp.bank.reqrespobj.unifiedorder.RetAtsUnifiedOrder;
import com.fingard.net.ConnState;
import com.fingard.net.WebRequest;
import com.fingard.text.StringHelper;
import com.fingard.util.DateHelper;
import com.fingard.util.SortHelper;
import com.fingard.xml.XmlTextReader;


public class BFB01QRPay extends BFB01Base implements IQRPay {
	
	public RetAtsGenQRInfo genQRInfo(ReqGenQRInfo p_req){
		RetAtsGenQRInfo retAts = new RetAtsGenQRInfo();
		try{
			retAts.rdSeq = p_req.rdSeq;
			ActSetItem actItem = this.getBankConfig().getActSetItemByAny(p_req.ownAct, p_req.merchantId, p_req.appId, "qr");
			if(actItem != null){
				
				retAts.respCode = "-1";
				retAts.respInfo = "邦付宝无此接口";
				retAts.qrInfo = "";
			}
			else{
				retAts.respCode = "-1";
				retAts.respInfo = "未找到账户或商户或公众号对应的配置";
			}
		}
		catch(Exception ex){
			retAts.respCode = "-1";
			retAts.respInfo = ex.getMessage();
		}
		return retAts;
	}
	
	public RetAtsUnifiedOrder submitQROrder(ReqUnifiedOrder p_req){
		RetAtsUnifiedOrder retAts = new RetAtsUnifiedOrder();
		try{
			retAts.rdSeq = p_req.rdSeq;
			ActSetItem actItem = this.getBankConfig().getActSetItemByAny(p_req.ownAct, p_req.merchantId, p_req.appId, "qr");
			if(actItem == null){
				retAts.respCode = "-1";
				retAts.payInfoCode = FailCode.E9981;
				retAts.respInfo = "未找到账户或商户或公众号对应的配置";
				retAts.transState = PaymentState.FAIL_Payment;
				return retAts;
			}

			StringBuilder sbLog = new StringBuilder();
			String merchantCert = CertHelper.getPublicKeyAsHexStr("PKCS12", actItem.ownKeyStorePath, actItem.ownKeyPassword, sbLog);
			if(StringHelper.isNullOrEmpty(merchantCert)){
				retAts.respCode = "-1";
				retAts.payInfoCode = FailCode.E9981;
				retAts.respInfo = "获取商户证书失败";
				if(sbLog.length() > 0){
					retAts.respInfo = retAts.respInfo+"："+sbLog.toString();
				}
				retAts.transState = PaymentState.FAIL_Payment;
				return retAts;
			}
			
			HashMap<String,String> mapParams = new HashMap<String,String>();
			mapParams.put("charset", this.getCharset());
			mapParams.put("version", "1.0");
			mapParams.put("signType", "RSA");
			mapParams.put("service", "GWDirectPay");//DirectPayment
			
			String pageReturnUrl = actItem.returnURL;
			if(StringHelper.isNullOrEmpty(pageReturnUrl)){
				pageReturnUrl = bankFront.getUrl("qrReturn");
			}
			mapParams.put("pageReturnUrl", pageReturnUrl);//交易结果通过页面跳转通知到这个url
			
			String offlineNotifyUrl = actItem.notifyURL;
			if(StringHelper.isNullOrEmpty(offlineNotifyUrl)){
				offlineNotifyUrl = bankFront.getUrl("qrNotify");
			}
			mapParams.put("offlineNotifyUrl", offlineNotifyUrl);
			
			mapParams.put("requestId", p_req.rdSeq);//合作商户请求的流水号，每次请求保持唯一
			mapParams.put("merchantId", actItem.merchantId);
			mapParams.put("orderId", p_req.productId);
			mapParams.put("orderTime", Format.DateTime14Format.format(p_req.getTimeStart()));
			mapParams.put("bankAbbr", actItem.bank);
			mapParams.put("totalAmount", StringHelper.amountMultiply100(p_req.amount));
			mapParams.put("currency", this.convertCurToBank(p_req.cur));
			
			double diffMins = DateHelper.getDifferMins(p_req.getTimeExpire(), p_req.getTimeStart());
			if(diffMins>=1000){
				double diffHours = DateHelper.getDifferHours(p_req.getTimeExpire(), p_req.getTimeStart());
				if(diffHours>=1000){
					double diffDays = DateHelper.getDifferDays(p_req.getTimeExpire(), p_req.getTimeStart());
					mapParams.put("validUnit", "02");/*只能取以下枚举值00-分  01-小时  02-日  03-月*/
					mapParams.put("validNum", Format.DecimalIntFormat.format(Math.floor(diffDays)));
				}
				else{
					mapParams.put("validUnit", "01");/*只能取以下枚举值00-分  01-小时  02-日  03-月*/
					mapParams.put("validNum", Format.DecimalIntFormat.format(Math.floor(diffHours)));
				}
			}
			else{
				mapParams.put("validUnit", "00");/*只能取以下枚举值00-分  01-小时  02-日  03-月*/
				mapParams.put("validNum", Format.DecimalIntFormat.format(Math.floor(diffMins)));
			}
			mapParams.put("productName", p_req.description);
			mapParams.put("productDesc", p_req.description);
			
			ArrayList<String> arrayParams = new ArrayList<String>();
			for (String key : mapParams.keySet()) {
				if(StringHelper.hasAnyChar(mapParams.get(key))){
					arrayParams.add(key+"="+mapParams.get(key));
				}
			}
			String[] signParams = new String[arrayParams.size()];
			arrayParams.toArray(signParams);
			String[] sortedValues = SortHelper.BubbleSort(signParams);
			StringBuilder sbValues = new StringBuilder();
	        for (int i = 0; i < sortedValues.length; i++){
	            if (i > 0){
	            	sbValues.append("&");
	            }
	            sbValues.append(sortedValues[i]);
	        }

	        byte[] bytesToSign = sbValues.toString().getBytes(getCharset());
	        
	        sbLog = new StringBuilder();
	        byte[] signResult = SignVerifyHelper.signData(bytesToSign, "PKCS12", "SHA1withRSA", actItem.ownKeyStorePath,
					actItem.ownKeyPassword, sbLog);
	        if(signResult!=null && sbLog.length()==0){
	        	
	        	String merchantSign = StringHelper.bytesToHexString(signResult);	        	
	        	StringBuilder sbPost = new StringBuilder();
	        	for (String eachKey : mapParams.keySet()) {
	        		if(sbPost.length()>0){
	        			sbPost.append("&");
	        		}
	        		sbPost.append(eachKey + "=" + URLEncoder.encode(mapParams.get(eachKey), getCharset()));
	    		}
	        	sbPost.append("&merchantCert="+merchantCert);
	        	sbPost.append("&merchantSign="+merchantSign);
	        	
	        	String qrUrl = bankFront.getUrl("qr");
	        	WriteBankLogLn2("下单地址：" + qrUrl);
	        	WriteBankLogStep2(sbPost);
	        	WebRequest tmpWebRequest = new WebRequest(qrUrl);
	        	tmpWebRequest.setConnectTimeout(20000);
	        	tmpWebRequest.setRequestProperty("User-Agent", "API");
	    		byte[] postBytes = sbPost.toString().getBytes(getCharset());
	    		
	    		String[] tmpStrRet = tmpWebRequest.upload(postBytes, getCharset());
	    		WriteBankLogStep3(tmpStrRet[1]);
	    		if(tmpStrRet[0].length() == 0){
	    			boolean verifyResult = true;//this.verifySign(tmpStrRet[1], actItem.saltKey, tmpSbMsg);
					if(verifyResult){
						retAts.respCode = "0";
						retAts.transState = "SUCCESS";
						retAts.customHashMap = new HashMap<String,String>();
						retAts.customHashMap.put("prepay_id", "");
						retAts.customHashMap.put("return_code", "");
						retAts.customHashMap.put("appid", "");
						retAts.customHashMap.put("mch_id", "");
						
						retAts.customHashMap.put("nonce_str", "");
						retAts.customHashMap.put("result_code", "SUCCESS");
						
						String[] retSignParams = new String[retAts.customHashMap.size()];
						int idx = 0;
						Iterator<String> iter = retAts.customHashMap.keySet().iterator();
			    		while (iter.hasNext()) {
			    			String eachKey = iter.next();
			    			retSignParams[idx] = eachKey+"="+retAts.customHashMap.get(eachKey);
			    			idx++;
			    		}
			    		String retSign = "";// this.signMessage(retSignParams, actItem.saltKey);
						retAts.customHashMap.put("sign", retSign);
					}
					else{
						retAts.respCode = "-1";
						retAts.respInfo = "验签失败";
					}
	    		}
	    		else if(tmpStrRet[0].equals(ConnState.FAIL)){
					retAts.respCode = "-1";
					retAts.respInfo = tmpStrRet[1];
					retAts.transState = PaymentState.FAIL_Payment;
					retAts.payInfoCode = tmpStrRet[0];
				}
				else{
					retAts.respCode = "-1";
					retAts.respInfo = tmpStrRet[1];
				}
	        }
	        else{
				retAts.respCode = "-1";
				retAts.payInfoCode = FailCode.E9981;
				retAts.respInfo = "签名失败：" + sbLog.toString();
				retAts.transState = PaymentState.FAIL_Payment;
				return retAts;
			}

		}
		catch(Exception ex){
			retAts.respCode = "-1";
			retAts.respInfo = ex.getMessage();
		}
		return retAts;
	}

	@SuppressWarnings("unchecked")
	public RetAtsQueryOrder queryQRPay(ReqQueryOrder p_req){
		RetAtsQueryOrder retAts = new RetAtsQueryOrder();
		try{
			retAts.rdSeq = p_req.rdSeq;
			ActSetItem actItem = this.getBankConfig().getActSetItemByAny(p_req.ownAct, p_req.merchantId, p_req.appId, "qr");
			if(actItem == null){
				retAts.respCode = "-1";
				retAts.respInfo = "未找到账户或商户或公众号对应的配置";
				return retAts;
			}
			@SuppressWarnings("rawtypes")
			ArrayList arrayParams = new ArrayList();
			
			StringBuilder sbXml = new StringBuilder();
			sbXml.append("<xml>\r\n");
			String appid = p_req.appId;
			if(StringHelper.isNullOrEmpty(appid)){
				appid = actItem.appId;
			}
			sbXml.append("  <appid>"+appid+"</appid>\r\n");
			arrayParams.add("appid="+appid);
			
			String mch_id = p_req.merchantId;
			if(StringHelper.isNullOrEmpty(mch_id)){
				mch_id = actItem.merchantId;
			}
			sbXml.append("  <mch_id>"+mch_id+"</mch_id>\r\n");
			arrayParams.add("mch_id="+mch_id);
			
			sbXml.append("  <out_trade_no>"+p_req.rdSeq+"</out_trade_no>\r\n");
			arrayParams.add("out_trade_no="+p_req.rdSeq);
			
			String time_stamp = DateHelper.getSecsFrom1970();
			//随机字符串 String(32)
			Random random=new Random();
			long randLong = random.nextLong();
			randLong = random.nextLong();
			String nonce_str = time_stamp+String.format("%20d", randLong);
			nonce_str = DigestHelper.cryptoMD5RetHexStr(nonce_str.getBytes());
			sbXml.append("  <nonce_str>"+nonce_str+"</nonce_str>\r\n");
			arrayParams.add("nonce_str="+nonce_str);
			
			String[] signParams = new String[arrayParams.size()];
			arrayParams.toArray(signParams);
			String sign = "";//this.signMessage(signParams, actItem.saltKey);
			sbXml.append("  <sign>"+sign+"</sign>\r\n");
			sbXml.append("</xml>");
			String[] tmpStrRet = new String[]{"",""};//sendToBank(sbXml.toString(), "orderquery");//发送
			if(tmpStrRet[0].length() == 0){
				StringBuilder tmpSbMsg = new StringBuilder();
				//解析银行返回
				XmlTextReader tmpBkReader = loadBankRespXmlReader(tmpStrRet[1], tmpSbMsg);
				if(tmpBkReader != null){
					
					String return_code = tmpBkReader.getFirstText("/xml/return_code");
					String return_msg = tmpBkReader.getFirstText("/xml/return_msg");
					if(return_code!= null && return_code.equals("SUCCESS")){
						boolean verifyResult = true;//this.verifySign(tmpStrRet[1], actItem.saltKey, tmpSbMsg);
						if(verifyResult){
							String result_code = tmpBkReader.getFirstText("/xml/result_code");
							String err_code = tmpBkReader.getFirstText("/xml/err_code");
							String trade_state = tmpBkReader.getFirstText("/xml/trade_state");
							retAts.respCode = "0";
							retAts.transState = StringHelper.joinNotEmpty("+", result_code, err_code, trade_state);
							String out_trade_no = tmpBkReader.getFirstText("/xml/out_trade_no");
							if(StringHelper.hasAnyChar(out_trade_no)){
								if(!out_trade_no.equals(p_req.rdSeq)){
									retAts.respCode = "-1";
									retAts.respInfo = "请求流水号"+p_req.rdSeq+"与响应流水号"+out_trade_no+"不一致";
									return retAts;
								}
							}
							retAts.rdSeq = p_req.rdSeq;

							retAts.payInfoCode = StringHelper.joinNotEmpty("+", err_code, trade_state);
							
							String err_code_des = tmpBkReader.getFirstText("/xml/err_code_des");
							String trade_state_desc = tmpBkReader.getFirstText("/xml/trade_state_desc");
							retAts.respInfo = StringHelper.joinNotEmpty("，", err_code_des, trade_state_desc);

							if(retAts.payInfoCode == null){
								retAts.payInfoCode = "";
							}
							if(retAts.respInfo == null){
								retAts.respInfo = "";
							}
						}
						else{
							retAts.respCode = "-1";
							retAts.respInfo = tmpSbMsg.toString();
						}
					}
					else{
						retAts.respCode = "-1";
						retAts.respInfo = return_msg;
					}
				}
				else{
					retAts.respCode = "-1";
					retAts.respInfo = tmpSbMsg.toString();
				}
			}
			else{
				retAts.respCode = "-1";
				retAts.respInfo = tmpStrRet[1];
			}
		}
		catch(Exception ex){
			retAts.respCode = "-1";
			retAts.respInfo = ex.getMessage();
		}
		return retAts;
	}
}
